/*
  This file is part of JCM.

  JCM is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  JCM is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with JCM.  If not, see <http://www.gnu.org/licenses/>.
*/
import java.util.*;
import java.math.*;

/**
 * Classe de representação de reforma monetária.
*/
public class Reforma
{
  /** Ano de aplicação da reforma monetária. */
  private final int ano;

  /** Mês de aplicação da reforma monetária. */
  private final int mes;

  /** Dia de aplicação da reforma monetária. */
  private final int dia;

  /**
   * Fator de conversão da reforma monetária de um padrão para outro mais recente.
  */
  private final BigDecimal FATOR;

  /** Comentário sobre a reforma. */
  private final String comentario;

  /** Indicador lógico do uso de centavos na moeda. */
  private final boolean useCents;

  /**
   * Construtor do objeto visando instância única.
   *
   * @param ano Ano da reforma.
   * @param mes Mês da reforma usando base 0 como feito no Java.
   * @param dia Dia da reforma.
   * @param fator Fator de conversão da reforma monetária.
   * @param useCents Indicador lógico de uso dos centavos.
   * @param comentario String contendo comentário sobre a reforma.
   * @return Instância do objeto.
  */
  private Reforma(int ano, int mes, int dia, String fator, boolean useCents, String comentario)
  {
    this.ano = ano;
    this.mes = mes;
    this.dia = dia;
    this.FATOR = fator.equals("1") ? BigDecimal.ONE : new BigDecimal(fator);
    this.useCents = useCents;
    this.comentario = comentario;
  }

  /**
   * Retorna a data normalizada dessa reforma monetária.
   *
   * @return A data dessa reforma monetária tal que os campos da data com
   *         granularidade temporal abaixo de dia são zero.
  */
  public Calendar getDate()
  {
    Calendar c = Calendar.getInstance();
    c.set(ano, mes, dia);
    c.set(Calendar.HOUR_OF_DAY, 0);
    c.set(Calendar.MINUTE, 0);
    c.set(Calendar.SECOND, 0);
    c.set(Calendar.MILLISECOND, 0);
    return c;
  }

  /**
   * Retorna o dia do mês dessa reforma monetária.
   *
   * @return O dia do mês dessa reforma monetária.
  */
  public int getDay() { return dia; }

  /**
   * Retorna o status de uso dos centavos a partir dessa reforma.
   *
   * @return O status de uso dos centavos a partir dessa reforma.
  */
  public boolean getUseCents() { return useCents; }

  /**
   * Atualiza valor monetário de data imediatamente anterior.
   *
   * @param value Valor monetário a atualizar.
   * @return O valor monetário atualizado.
  */
  public BigDecimal transform(BigDecimal value)
  {
    return useCents ? value.divide(FATOR, MathContext.DECIMAL64)
      : value.divideToIntegralValue(FATOR);
  }

  /**
   * Retorna comentário sobre a reforma monetária.
   *
   * @return O comentário sobre a reforma monetária.
  */
  public String getComment() { return comentario; }

  /**
   * Pesquisa se a data fornecida coincide em ano e mês
   * com alguma data de reforma.
   *
   * @param c Data alvo da pesquisa.
   * @return O índice da data de reforma coincidente ou -1 se não encontrar.
  */
  static public int search(Calendar c)
  {
    int index = -1;
    for (int j=reformas.length-1; (j >= 0) && (index < 0); j--)
    {
      if ((c.get(Calendar.YEAR) == reformas[j].ano)
          && (c.get(Calendar.MONTH) == reformas[j].mes)) index = j;
      if (c.get(Calendar.YEAR) >= reformas[j].ano) break;
    }
    return index;
  }

  /**
   * Pesquisa o status de uso dos centavos numa data.
   *
   * @param c A data a pesquisar.
   * @return O status de uso dos centavos na data.
  */
  static public boolean useCentsFrom(Calendar c)
  {
    long t = c.getTimeInMillis();
    // índice da data mais moderna
    int index = reformas.length - 1;
    // loop de pesquisa buscando a última data anterior à data fornecida
    while ((index > 0) && (t < reformas[index].getDate().getTimeInMillis())) index--;
    return reformas[index].useCents;
  }

  /**
   * Array representando a série histórica das reformas monetárias do
   * Brasil.
  */
  static final public Reforma[] reformas =
  {
    new Reforma(1942, Calendar.NOVEMBER, 01, "1000", true,
      "Réis para Cruzeiro (1:000$100 = Cr$ 1,10)"),
    new Reforma(1964, Calendar.DECEMBER, 02, "1", false,
      "Cruzeiro para Cruzeiro (Cr$ 1,00 = Cr$ 1)"),
    new Reforma(1967, Calendar.FEBRUARY, 13, "1000", true,
      "Cruzeiro para Cruzeiro Novo (Cr$ 1.000 = NCr$ 1,00)"),
    new Reforma(1970, Calendar.MAY,      15, "1", true,
      "Cruzeiro Novo para Cruzeiro (NCr$ 1,00 = Cr$ 1,00)"),
    new Reforma(1984, Calendar.AUGUST,   16, "1", false,
      "Cruzeiro para Cruzeiro (Cr$ 1,00 = Cr$ 1)"),
    new Reforma(1986, Calendar.FEBRUARY, 28, "1000", true,
      "Cruzeiro para Cruzado (Cr$ 1.000 = Cz$ 1,00)"),
    new Reforma(1989, Calendar.JANUARY,  16, "1000", true,
      "Cruzado para Cruzado Novo (Cz$ 1.000,00 = NCz$ 1,00)"),
    new Reforma(1990, Calendar.MARCH,    16, "1", true,
      "Cruzado Novo para Cruzeiro (NCz$ 1,00 = Cr$ 1,00)"),
    new Reforma(1993, Calendar.AUGUST,   01, "1000", true,
      "Cruzeiro para Cruzeiro Real (Cr$ 1.000,00 = CR$ 1,00)"),
    new Reforma(1994, Calendar.JULY,     01, "2750", true,
      "Cruzeiro Real para Real (CR$ 2.750,00 = R$ 1,00)")
  };
}
